L08: Proudy a přesměrování

linux.edumach.cz



1. Východiska

Jednou z prvních aplikací v historii systémů UNIX bylo zpracování textu. Proto v něm naleznete velké množství programů pro manipulaci s textovými soubory, jejich třídění, prohledávání, porovnávání a formátování. Tímto se nutně nemyslí "textové editory".

99,9 % všech činností, které se postupně naučíme a budeme procvičovat se týkají manipulace s prostými textovými soubory a adresáři (a i ty jsou v unixových systémech soubory). V unixových systémech platí: "V Linuxu je vše soubor. Pokud ne, je to proces".

Jak je v unixových systémech zvykem, většina programů je jednoúčelová a svůj výstup posílají na standardní výstup. Postupně se naučíme využívat i přesměrování a roury k propojení standardních vstupů a výstupů.

2. Příkaz cat

Příkaz cat (concatenate) je velmi užitečný a má několik způsobů použití.

Spuštěný bez argumentu opisuje text ze standardního vstupu (stdin, klávesnice) na standardní výstup (stdout, terminál). Pro ukončení stiskněte zkratku Ctrl+D (na macOS control⌃+D).

$ cat

Standardní výstup jde přesměrovat do souboru. Spusťte příkaz níže, napište několik řádků textu a ukončete. Text se zapsal do souboru dopis.

$ cat > dopis

Dvě důležité poznámky:

  1. Možnost přesměrování vstupu a výstupu je v unixových systémech skvělý "vynález". Budeme se jim věnovat průběžně. Je natolik důležitý, že jej v omezené míře podporuje i terminál ve Windows.

  2. Pokud vás překvapilo, že soubor dopis nemá příponu, pak i to je v unixových systémech zcela běžné. Tyto systémy nepotřebují přípony k rozpoznání typu souboru, jak je tomu například ve Windows. Namísto toho se spoléhají na obsah souboru a metadata uložená v jeho hlavičce.

Nyní si obsah souboru vypíšeme, opět příkazem cat:

$ cat dopis

Teď je standardním vstupem (stdin) soubor dopis, výstupem terminál (stdout).

Stejným postupem (přesměrováním do souboru) si vytvořte ještě dva soubory dopis_2 a dopis_3. Do všech tří souborů přidejte na konec několik řádků textu (pomocí dvou šipek):

$ cat >> dopis

Pro kontrolu si obsah souborů vypište.

$ cat dopis
$ cat dopis_2
$ cat dopis_3

3. Datové proudy (deskriptory)

⚠️ Pochopení principu proudů je pro práci v terminálu životně důležité.

Ve všech unixových systémech je zavedena dohoda, podle které je nově spuštěný program v terminálu standardně vybaven třemi otevřenými datovými proudy (deskriptory):

Vysvětlení:

Proč jen "dohoda"? Každý příkaz, který spouštíme někdo musel vytvořit -- naprogramovat v jazyce C. Tvůrce do něj musel práci s proudy zakomponovat -- není to automatická vlastnost systému jako takového. Ten jen programu při spuštění ty proudy přiřadí. Zda je příkaz využije je na něm. Nijak se tím nezatěžujte, všechny příkazy, které budeme používat umí s proudy pracovat.

Pokud tedy program potřebuje získat data, čte je z proudu 0 (stdin). Většina příkazů je připravena (byla svými tvůrci takto vytvořena) číst obsah souboru předaného jako argument. To jsme si už vysvětlili výše:

$ cat dopis

Potřebuje-li příkaz něco vypsat, použije proud 1 (stdout). Toto přesně udělal příkaz cat: vypsal obsah souboru dopis na terminál.

Pokud ale příkaz vyvolá chybu, pak se použije proud 2 (stderr). Ve výchozím nastavení se i tento proud směruje na terminál:

$ cat dopys
cat: dopys: No such file or directory

Možná se ptáte, proč tedy existují dva proudy (stdout a stderr), jejichž výstup je stejný – oba směrují svůj výstup na terminál? Důvodem je, že každý z proudů je možné směrovat do dvou směrů (např. souborů). K tomu, jak se to dělá se dostaneme později.

4. Přesměrování výstupu

Znaky > a >> se přesměruje standardní výstup do souboru.

V obou případech platí, že pokud soubor neexistuje, vytvoří se.

Nejsou to všechny varianty přesměrování. Později si ukážeme další.

Kromě předchozích typů přesměrování > a >>, které už známe, existuje ještě několik dalších:

Poznámka: ta dvojka je číslo datového proudu stderr.

příkaz 2> soubor 
příkaz 2>> soubor 
příkaz &> soubor 

5. Přesměrování vstupu

Většina příkazů v je ve výchozím stavu "nastavena" na čtení vstupu. Například

$ cat soubor 

Znak < přesměruje standardní vstup ze souboru:

příkaz < soubor 

Jinými slovy, příkazy

$ cat soubor 
$ cat < soubor 

dělají to samé.

Tato konstrukce se častěji používá ve skriptech, kdy má nějaký příkaz postupně načítat data ze souboru:

while read i 
do 
  echo $i #vypisuje radky ze souboru data.txt 
done < data.txt 

Předchozí skript jde napsat i bez přesměrování vstupu:

for i in $(cat data.txt) 
do 
  echo $i #vypisuje radky ze souboru data.txt 
done 

Příklady pěkně demonstrují sílu terminálu a skriptů. Stejná činnost jde napsat různým způsobem.

V tuto chvíli je zde přesměrování vstupu jen pro úplnost. Později se k němu vrátíme při tvorbě skriptů.

6. Kombinace přesměrování

Příkaz du -s <adresar> vypíše celkovou velikost adresáře. Na ukázku vypíšeme adresář /etc. Nejprve na stdout a poté do nějakého souboru, např. etc_out:

$ du -s /etc 
$ du -s /etc > etc_out 

V obou případech se nepřesměruje stderr. Ten je nutné přesměrovat samostatně:

$ du -s /etc 2> etc_out 
$ du -s /etc > etc_out 2> etc_err 

6.1. Speciální soubor /dev/null

Pokud se stderr chceme prostě jen zabavit, pošleme do speciálního souboru /dev/null. V něm nenávratně zmizí (soubor /dev/null se chová jako „černá díra“).

$ du -s /etc 2> /dev/null 
$ du -s /etc > etc_out 2> /dev/null 

⚠️ Poznámka: Nezaměňujte zahazování proudu dat (/dev/null) s mazáním souboru (rm). Není to totéž.

7. 💾 Úkoly

  1. Sestavte příkaz, který do souboru vypis_trida přesměruje (zapíše) názvy adresářů vaší třídy (těch z adresáře /home).

  2. Proveďte totéž do souboru vypis_trida_desc s tím rozdílem, že názvy budou v opačném pořadí (to zajistí vhodný přepínač příkazu ls).

  3. Napište příkaz, který vypíše seznam souborů v aktuálním adresáři (včetně skrytých) a výstup zapíše (přepíše) do souboru seznam.txt.

  4. Máte již existující soubor log.txt. Chcete do něj přidat (na konec) výpis příkazu date (aktuální datum a čas). Jaký příkaz použijete?

  5. Máte příkaz, který může generovat chybové hlášení (stderr). Napište příkaz, který přesměruje standardní výstup (stdout) do vystup.txt a zároveň chybový výstup do chyby.txt.

  6. Jak zapisujete příkaz, kterým chcete všechno (stdout i stderr) dohromady přesměrovat do jednoho souboru komplet.txt?

  7. Chcete přesměrovat obsah souboru data.txt jako standardní vstup pro příkaz sort. Jak by vypadal příkaz?

  8. Jak by vypadal příkaz, který provede ls /neexistujici_adresar, přesměruje chybový výstup do „černé díry“ /dev/null, ale nechá standardní výstup normálně na obrazovce?